﻿using System;
using System.Collections.Generic;
using System.Text;

namespace JustePort
{
    public class BitBuffer
    {
        // Frame size is set as 4096 samples, 16 bit, stereo
        public const int FrameSize = 4096 * 2 * 2;

        byte[] buffer;

        byte[] masks = 
        {
            0x01, 0x03, 0x07, 0x0F,
            0x1F, 0x3F, 0x7F, 0xFF
        };

        int bitOffset = 0;
        int byteOffset = 0;

        public byte[] Buffer
        {
            get
            {
                return this.buffer;
            }
        }

        public BitBuffer(int length)
        {
            buffer = new byte[length];
        }

        public static byte[] EncodeALAC(byte[] buffer)
        {
            BitBuffer bitbuf = new BitBuffer(FrameSize + 3);

            bitbuf.WriteBits(1, 3);  // channels -- 0 mono, 1 stereo
            bitbuf.WriteBits(0, 4);  // unknown
            bitbuf.WriteBits(0, 12); // unknown
            bitbuf.WriteBits(0, 1);  // 'has size' flag
            bitbuf.WriteBits(0, 2);  // unknown
            bitbuf.WriteBits(1, 1);  // 'no compression' flag

            for (int i = 0; i < buffer.Length; i += 2)
            {
                // endian swap 16 bit samples
                bitbuf.WriteBits(buffer[i + 1], 8);
                bitbuf.WriteBits(buffer[i], 8);
            }

            return bitbuf.Buffer;
        }

        public void WriteBits(int data, int numbits)
        {
            if (bitOffset != 0 && bitOffset + numbits > 8)
            {
                int numwritebits = 8 - bitOffset;
                byte bitstowrite = (byte)((data >> (numbits - numwritebits)) <<
                                   (8 - bitOffset - numwritebits));
                buffer[byteOffset] |= bitstowrite;
                numbits -= numwritebits;
                bitOffset = 0;
                byteOffset++;
            }

            while (numbits >= 8)
            {
                byte bitstowrite = (byte)((data >> (numbits - 8)) & 0xFF);
                buffer[byteOffset] |= bitstowrite;
                numbits -= 8;
                bitOffset = 0;
                byteOffset++;
            }

            if (numbits > 0)
            {
                byte bitstowrite = (byte)((data & masks[numbits]) <<
                                   (8 - bitOffset - numbits));
                buffer[byteOffset] |= bitstowrite;
                bitOffset += numbits;
                if (bitOffset == 8)
                {
                    byteOffset++;
                    bitOffset = 0;
                }
            }
        }
    }
}